1<script lang="ts">
  2	import { goto, replaceState } from '$app/navigation';
  3	import { page } from '$app/state';
  4	import { afterNavigate } from '$app/navigation';
  5	import { ChatScreen, DialogModelNotAvailable } from '$lib/components/app';
  6	import { chatStore, isLoading } from '$lib/stores/chat.svelte';
  7	import {
  8		conversationsStore,
  9		activeConversation,
 10		activeMessages
 11	} from '$lib/stores/conversations.svelte';
 12	import { modelsStore, modelOptions, selectedModelId } from '$lib/stores/models.svelte';
 13
 14	let chatId = $derived(page.params.id);
 15	let currentChatId: string | undefined = undefined;
 16
 17	// URL parameters for prompt and model selection
 18	let qParam = $derived(page.url.searchParams.get('q'));
 19	let modelParam = $derived(page.url.searchParams.get('model'));
 20
 21	// Dialog state for model not available error
 22	let showModelNotAvailable = $state(false);
 23	let requestedModelName = $state('');
 24	let availableModelNames = $derived(modelOptions().map((m) => m.model));
 25
 26	// Track if URL params have been processed for this chat
 27	let urlParamsProcessed = $state(false);
 28
 29	/**
 30	 * Clear URL params after message is sent to prevent re-sending on refresh
 31	 */
 32	function clearUrlParams() {
 33		const url = new URL(page.url);
 34		url.searchParams.delete('q');
 35		url.searchParams.delete('model');
 36		replaceState(url.toString(), {});
 37	}
 38
 39	async function handleUrlParams() {
 40		// Ensure models are loaded first
 41		await modelsStore.fetch();
 42
 43		// Handle model parameter - select model if provided
 44		if (modelParam) {
 45			const model = modelsStore.findModelByName(modelParam);
 46			if (model) {
 47				try {
 48					await modelsStore.selectModelById(model.id);
 49				} catch (error) {
 50					console.error('Failed to select model:', error);
 51					requestedModelName = modelParam;
 52					showModelNotAvailable = true;
 53					return;
 54				}
 55			} else {
 56				// Model not found - show error dialog
 57				requestedModelName = modelParam;
 58				showModelNotAvailable = true;
 59				return;
 60			}
 61		}
 62
 63		// Handle ?q= parameter - send message in current conversation
 64		if (qParam !== null) {
 65			await chatStore.sendMessage(qParam);
 66			// Clear URL params after message is sent
 67			clearUrlParams();
 68		} else if (modelParam) {
 69			// Clear params even if no message was sent (just model selection)
 70			clearUrlParams();
 71		}
 72
 73		urlParamsProcessed = true;
 74	}
 75
 76	async function selectModelFromLastAssistantResponse() {
 77		const messages = activeMessages();
 78		if (messages.length === 0) return;
 79
 80		let lastMessageWithModel: DatabaseMessage | undefined;
 81
 82		for (let i = messages.length - 1; i >= 0; i--) {
 83			if (messages[i].model) {
 84				lastMessageWithModel = messages[i];
 85				break;
 86			}
 87		}
 88
 89		if (!lastMessageWithModel) return;
 90
 91		const currentModelId = selectedModelId();
 92		const currentModelName = modelOptions().find((m) => m.id === currentModelId)?.model;
 93
 94		if (currentModelName === lastMessageWithModel.model) {
 95			return;
 96		}
 97
 98		const matchingModel = modelOptions().find(
 99			(option) => option.model === lastMessageWithModel.model
100		);
101
102		if (matchingModel) {
103			try {
104				await modelsStore.selectModelById(matchingModel.id);
105				console.log(`Automatically loaded model: ${lastMessageWithModel.model} from last message`);
106			} catch (error) {
107				console.warn('Failed to automatically select model from last message:', error);
108			}
109		}
110	}
111
112	afterNavigate(() => {
113		setTimeout(() => {
114			selectModelFromLastAssistantResponse();
115		}, 100);
116	});
117
118	$effect(() => {
119		if (chatId && chatId !== currentChatId) {
120			currentChatId = chatId;
121			urlParamsProcessed = false; // Reset for new chat
122
123			// Skip loading if this conversation is already active (e.g., just created)
124			if (activeConversation()?.id === chatId) {
125				// Still handle URL params even if conversation is active
126				if ((qParam !== null || modelParam !== null) && !urlParamsProcessed) {
127					handleUrlParams();
128				}
129				return;
130			}
131
132			(async () => {
133				const success = await conversationsStore.loadConversation(chatId);
134				if (success) {
135					chatStore.syncLoadingStateForChat(chatId);
136
137					// Handle URL params after conversation is loaded
138					if ((qParam !== null || modelParam !== null) && !urlParamsProcessed) {
139						await handleUrlParams();
140					}
141				} else {
142					await goto('#/');
143				}
144			})();
145		}
146	});
147
148	$effect(() => {
149		if (typeof window !== 'undefined') {
150			const handleBeforeUnload = () => {
151				if (isLoading()) {
152					console.log('Page unload detected while streaming - aborting stream');
153					chatStore.stopGeneration();
154				}
155			};
156
157			window.addEventListener('beforeunload', handleBeforeUnload);
158
159			return () => {
160				window.removeEventListener('beforeunload', handleBeforeUnload);
161			};
162		}
163	});
164</script>
165
166<svelte:head>
167	<title>{activeConversation()?.name || 'Chat'} - llama.cpp</title>
168</svelte:head>
169
170<ChatScreen />
171
172<DialogModelNotAvailable
173	bind:open={showModelNotAvailable}
174	modelName={requestedModelName}
175	availableModels={availableModelNames}
176/>